use serde::{Deserialize, Serialize};
use axum::{
    async_trait, extract::FromRequestParts, 
    http::{request::Parts, StatusCode}, RequestPartsExt,
    response::{IntoResponse, Response},
    Json,
};
use axum_extra::{
    headers::{authorization::Bearer, Authorization},
    TypedHeader,
};
use jsonwebtoken::{decode, DecodingKey, EncodingKey, Validation};
use once_cell::sync::Lazy;


pub const KEYS: Lazy<Keys> = Lazy::new(|| {
    let secret = std::env::var("JWT_SECRET").expect("JWT_SECRET must be set");
    Keys::new(secret.as_bytes())
});


pub struct Keys {
    pub encoding: EncodingKey,
    pub decoding: DecodingKey,
}

impl Keys {
    pub fn new(secret: &[u8]) -> Keys {
        Keys {
            encoding: EncodingKey::from_secret(secret),
            decoding: DecodingKey::from_secret(secret),
        }
    }
}


#[derive(Debug, Serialize, Deserialize)]
pub struct Claims {
    sub: String,    //用于存储user id
    exp: usize,     //过期时间戳
} 

impl Claims {
    pub fn new(user_id: String) -> Claims {
        Claims {
            sub: user_id,
            exp: get_epoch() + 15 * 24 * 60 * 60,
        }
    }

    pub fn get_user_id(&self) -> i32 {
        self.sub.parse::<i32>().unwrap()
    }
}


#[async_trait]
impl<S> FromRequestParts<S> for Claims 
where
    S: Send + Sync,
{
    type Rejection = AuthError;

    async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
        let TypedHeader(Authorization(bearer)) = parts
            .extract::<TypedHeader<Authorization<Bearer>>>()
            .await
            .map_err(|_| AuthError::InvalidToken)?;

        let token_data = decode::<Claims>(
            bearer.token(),
            &KEYS.decoding,
            &Validation::default(),
        ).map_err(|_| AuthError::InvalidToken)?;

        Ok(token_data.claims)
    }
}



fn get_epoch() -> usize {
    std::time::SystemTime::now() //获取当前时间戳
        .duration_since(std::time::UNIX_EPOCH) //计算从 UNIX 纪元到当前系统时间的时间差
        .unwrap()
        .as_secs() as usize
}



pub enum AuthError {
    WrongCredentials,
    MissingCredentials,
    TokenCreation,
    InvalidToken,
}

impl IntoResponse for AuthError {
    fn into_response(self) -> Response {
        let (status, error_message) = match self {
            AuthError::WrongCredentials => (StatusCode::UNAUTHORIZED, "Wrong credentials"),
            AuthError::MissingCredentials => (StatusCode::UNAUTHORIZED, "Missing credentials"),
            AuthError::TokenCreation => (StatusCode::INTERNAL_SERVER_ERROR, "Token creation error"),
            AuthError::InvalidToken => (StatusCode::UNAUTHORIZED, "Invalid token"),
        };
        let body = Json(serde_json::json!({
            "error": error_message
        }));
        (status, body).into_response()
    }
}


